import { Injectable } from '@nestjs/common';
import { randomUUID, createHash } from 'crypto'
import { Merchant } from 'src/entities/Merchant';
import { MerchantModelService } from 'src/model/merchant-model/merchant-model.service';
import { PermissionRoleModelService } from 'src/model/permission-role-model/permission-role-model.service';
import { PermissionRuleModelService } from 'src/model/permission-rule-model/permission-rule-model.service';
import { DistrictService } from 'src/modules/district/district.service';
import { MerchantResult } from 'src/type/Merchant.result';
import { getTime, isPlatform } from 'src/utils';
import { FindConditions, In, Like } from 'typeorm';
import { AddMerchantDto, EditMerchantDto, MerchantListDto } from './dto';

@Injectable()
export class MerchantService {
    constructor(
        private merchantModelService:MerchantModelService,
        private districtService:DistrictService,
        private roleModel:PermissionRoleModelService,
        private ruleModel:PermissionRuleModelService,
    ) {}

    async merchantList(dto:MerchantListDto) {
        let where:FindConditions<Merchant> = {}

        if(dto.merchantName) {
            where.merchantName = Like(`%${dto.merchantName}%`)
        }

        if(dto.account) {
            where.account = dto.account
        }

        let result:PaginationResult<MerchantResult> = {
            total : 0,
            rows : []
        }
        result.total = await this.merchantModelService.count(where)
        if(result.total > 0) {
            const fields:(keyof Merchant)[] = ['merchantId', 'merchantName', 'account', 'status', 'profitRate', 'withdrawalRate', 'contact', 'phone', 'provinceId', 'cityId', 'districtId', 'address']
            result.rows = await this.merchantModelService.pagination(dto.pageNo, dto.pageSize, where, fields, {merchantId : 'DESC'})

            const districtIds:number[] = []

            result.rows.forEach(item => {
                if(item.provinceId > 0 && !districtIds.includes(item.provinceId)) {
                    districtIds.push(item.provinceId)
                }
                if(item.cityId > 0 && !districtIds.includes(item.cityId)) {
                    districtIds.push(item.cityId)
                }
                if(item.districtId > 0 && !districtIds.includes(item.districtId)) {
                    districtIds.push(item.districtId)
                }
            })

            if(districtIds.length > 0) {
                const districtMap = await this.districtService.getDistrictMapByIds(districtIds)
                result.rows.forEach(item => {
                    item.province = districtMap[item.provinceId]
                    item.city = districtMap[item.cityId]
                    item.district = districtMap[item.districtId]
                })
            }

        }

        return result
    }

    async queryMerchantName(name:string, roleType:RoleType, userId:number) {
        let where:FindConditions<Merchant> = {merchantName : Like(name + '%')}
        where.parentId = isPlatform(roleType) ? 0 : userId
        let rows = await this.merchantModelService.find(where, ['merchantName', 'merchantId'], 0, 10, {merchantId : 'DESC'})
        return rows
    }

    accountIsExists(merchantName:string, account:string) {
        return this.merchantModelService.isExistsByWhere({merchantName, account : account})
    }

    async add(dto:AddMerchantDto) {
        const time = getTime()
        const salt = randomUUID().split('-').pop()
        dto.password = createHash('md5').update(dto.password + salt).digest('hex').toLowerCase()
        if(dto.profitRate) {
            dto.profitRate = dto.profitRate
        }
        
        dto.withdrawalRate = String(dto.withdrawalRate)
        
        let id = await this.merchantModelService.insert({...dto, createTime : getTime(), salt})
        return id
    }

    async edit(merchantId:number, dto:EditMerchantDto) {
        let data:{[key in keyof Merchant]? : Merchant[key]} = {}
        let fields = Object.keys(dto) as (keyof Merchant)[]
        const merchant = await this.merchantModelService.findOne({merchantId : merchantId}, fields)
        if(!merchant) return false

        for(let k in merchant) {
            if(JSON.stringify(merchant[k]) != JSON.stringify(dto[k])) {
                data[k] = dto[k]
            }
        }

        if(Object.keys(data).length == 0) return true

        data.updateTime = getTime()
        let affectedRows = await this.merchantModelService.update({merchantId : merchantId}, data)
        return affectedRows ? true : false
    }

    getMerchantEditData(merchantId:number) {
        return this.merchantModelService.findOne({merchantId}, ['merchantName', 'profitRate', 'withdrawalRate', 'roles', 'contact', 'phone', 'provinceId', 'cityId', 'districtId', 'address', 'remark'])
    }


    updateMerchantStatus(merchantId:number, status:0|1) {
        return this.merchantModelService.update({ merchantId }, { status:status})
    }


    async checkeAuth(merchantId:number, url:string) {
        const merchant = await this.merchantModelService.findOne({merchantId}, ['roles'])
        if(!merchant) return false

        const roleIds = merchant.roles as number[]

        if(!roleIds || roleIds.length == 0) return false

        const rows = await this.roleModel.find({id : In(roleIds)}, ['rules'])
        
        if(!rows || rows.length == 0) return false

        const ruleIds = []

        rows.forEach(row => {
            if(row.rules) {
                const rules = row.rules as number[]
                ruleIds.push(...rules)
            }
        } )

        const rules = await this.ruleModel.find({id : In(Array.from(new Set(ruleIds)))}, ['urls'])

        for(let i = 0; i < rules.length; i ++) {
            const urls = rules[i].urls as string[]
            if(urls && urls.includes(url)) return true
        }

        return false
    }
}
